/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package linguaggio;

import java.util.LinkedList;
import java.util.ListIterator;
import linguaggio.Grammatica.ErrorType;

/**
 *
 * @author valerio
 */
public class Controller {
    //TODO verifiche da effettuare:
    /*
     * controllare se ogni non terminale non ha soltanto la produzione ricorsiva
     * controllare anche il caso A->B B->A eccezione nel rimuovi di precedenza
     * token DONE controllare la correttezza delle sincrotriple
     */

    private static final EmptyToken eps = new EmptyToken();

    public static String control(Grammatica g) {
        LinkedList<NonTerminale> nonTerminali = g.getNonTerminali();
        ListIterator<NonTerminale> it = nonTerminali.listIterator();
        NonTerminale nt;
        while (it.hasNext()) {
            nt = it.next();
            if (nt.getProduzioni().isEmpty()) {
                return "Il Terminale " + nt.getName() + " non ha definita alcuna produzione"; //Caso nessuna produzione
            }
        }
        return "";
    }

    public static String controlSincrotripla(Grammatica g, SincroTripla s) {
        String result = "";
        if (!s.getMarcaApertura().isEmpty()) {
            LinkedList<Token> first = getFirst(g, s.getNonTerminale());
            if (!first.containsAll(s.getMarcaApertura())) {
                ListIterator<Token> it = s.getMarcaApertura().listIterator();
                Token t;
                while (it.hasNext()) {
                    t = it.next();
                    if (!first.contains(t)) {
                        result += "Il token " + t + " contenuto nella marca di apertura non fa parte dei FIRST di " + s.getNonTerminale() + "\n";
                    }
                }
            }
        }
        if (!s.getMarcaChiusura().isEmpty()) {
            LinkedList<Token> follow = getFollow(g, s.getNonTerminale());
            System.out.println("follows: " + follow);
            if (!follow.containsAll(s.getMarcaChiusura())) {
                ListIterator<Token> it = s.getMarcaChiusura().listIterator();
                Token t;
                while (it.hasNext()) {
                    t = it.next();
                    if (!follow.contains(t)) {
                        result += "Il token " + t + " contenuto nella marca di chiusura non fa parte dei FOLLOW di " + s.getNonTerminale() + "\n";
                    }
                }
            }
        }
        return result;
    }

    public static LinkedList<Token> getFirst(Grammatica g, NonTerminale nt) {
        LinkedList<Token> first = new LinkedList<>();
        ListIterator<Produzione> it = nt.getProduzioni().listIterator();
        Produzione p;
        Element e;
        while (it.hasNext()) {
            p = it.next();
            if (p.getParteDestra().isEmpty()) {
                if (!first.contains(eps)) {
                    first.add(eps);
                }
            } else {
                e = p.getParteDestra().getFirst();
                if (e.getClass().equals(Token.class)) {
                    first.add((Token) e);
                } else if (e.getClass().equals(NonTerminale.class) && !e.equals(nt)) {
                    System.out.println(e);
                    LinkedList<Token> firsts = getFirst(g, (NonTerminale) e);
                    ListIterator<Token> lit = firsts.listIterator();
                    while (lit.hasNext()) {
                        Token t = lit.next();
                        if (first.contains(t)) {
                            lit.remove();
                        }
                    }
                    first.addAll(firsts);
                }
            }
        }
        return first;
    }

    public static LinkedList<Token> getFollow(Grammatica g, NonTerminale nt) {
        LinkedList<Token> follow = new LinkedList<>();
        ListIterator<NonTerminale> ntit = g.getNonTerminali().listIterator();
        ListIterator<Produzione> pit;
        Produzione p;
        while (ntit.hasNext()) {
            pit = ntit.next().getProduzioni().listIterator();
            while (pit.hasNext()) {
                p = pit.next();
                LinkedList<Integer> indexes = indexesOf(p.getParteDestra(), nt);
                ListIterator<Integer> indexesIterator = indexes.listIterator();
                int index;
                while (indexesIterator.hasNext()) {
                    index = indexesIterator.next();

                    if (index + 1 < p.getParteDestra().size()) {
                        LinkedList<Token> follows = getFirst(g, new LinkedList<>(p.getParteDestra().subList(index + 1, p.getParteDestra().size())));
                        ListIterator<Token> fit = follows.listIterator();
                        Token t;
                        while (fit.hasNext()) {
                            t = fit.next();
                            if (follow.contains(t)) {
                                fit.remove();
                            }
                        }
                        follow.addAll(follows);
                        //System.out.println("FIRSTS: "+follows);

                    } else {
                        //index = p.getParteDestra().subList(index + 1, p.getParteDestra().size()).indexOf(nt);
                    }
                }
            }
        }
        return follow;
    }

    public static LinkedList<Token> getFirst(Grammatica g, LinkedList<Element> string) {
        LinkedList<Token> first = new LinkedList<>();
        Element e;
        e = string.getFirst();
        if (e.getClass().equals(Token.class)) {
            first.add((Token) e);
        } else if (e.getClass().equals(NonTerminale.class)) {
            LinkedList<Token> firsts = getFirst(g, (NonTerminale) e);
            if (!firsts.contains(eps)) {
                ListIterator<Token> lit = firsts.listIterator();
                while (lit.hasNext()) {
                    Token t = lit.next();
                    if (first.contains(t)) {
                        lit.remove();
                    }
                }
                first.addAll(firsts);
            } else {
                firsts.remove(eps);
                ListIterator<Token> lit = firsts.listIterator();
                while (lit.hasNext()) {
                    Token t = lit.next();
                    if (first.contains(t)) {
                        lit.remove();
                    }
                }
                firsts.addAll(firsts);
                if (string.size() > 1) {
                    firsts = getFirst(g, new LinkedList<>(string.subList(1, string.size())));
                    lit = firsts.listIterator();
                    while (lit.hasNext()) {
                        Token t = lit.next();
                        if (first.contains(t)) {
                            lit.remove();
                        }
                    }
                    first.addAll(firsts);
                }
            }
        }
        return first;
    }

    public static LinkedList<Integer> indexesOf(LinkedList<? extends Object> list, Object obj) {
        LinkedList<Integer> result = new LinkedList<>();
        ListIterator it = list.listIterator();
        int index;
        while (it.hasNext()) {
            index = it.nextIndex();
            if (it.next().equals(obj)) {
                result.add(index);
            }
        }
        return result;
    }
}